Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(deps): bump eslint-plugin-react from 7.35.0 to 7.37.2 #1359

Merged

Conversation

dependabot[bot]
Copy link
Contributor

@dependabot dependabot bot commented on behalf of github Nov 1, 2024

Bumps eslint-plugin-react from 7.35.0 to 7.37.2.

Release notes

Sourced from eslint-plugin-react's releases.

v7.37.2

Fixed

  • [destructuring-assignment]: fix false negative when using typeof props.a (#3835[] @​golopot)

Changed

  • [Refactor] [destructuring-assignment]: use getParentStatelessComponent (#3835[] @​golopot)

#3835: jsx-eslint/eslint-plugin-react#3835 [destructuring-assignment]: docs/rules/destructuring-assignment.md

v7.37.1

Fixed

Changed

#3834: jsx-eslint/eslint-plugin-react#3834 #3836: jsx-eslint/eslint-plugin-react#3836

v7.37.0

Added

Changed

#3805: jsx-eslint/eslint-plugin-react#3805 #3824: jsx-eslint/eslint-plugin-react#3824 #3826: jsx-eslint/eslint-plugin-react#3826 #3830: jsx-eslint/eslint-plugin-react#3830 #3831: jsx-eslint/eslint-plugin-react#3831 [forbid-component-props]: docs/rules/forbid-component-props.md [no-unescaped-entities]: docs/rules/no-unescaped-entities.md [no-unstable-nested-components]: docs/rules/no-unstable-nested-components.md

v7.36.1

Fixed

#3823: jsx-eslint/eslint-plugin-react#3823 #3821: jsx-eslint/eslint-plugin-react#3821

... (truncated)

Changelog

Sourced from eslint-plugin-react's changelog.

7.37.2 - 2024.10.22

Fixed

  • [destructuring-assignment]: fix false negative when using typeof props.a (#3835[] @​golopot)

Changed

  • [Refactor] [destructuring-assignment]: use getParentStatelessComponent (#3835[] @​golopot)

#3835: jsx-eslint/eslint-plugin-react#3835

7.37.1 - 2024.10.01

Fixed

Changed

#3836: jsx-eslint/eslint-plugin-react#3836 #3834: jsx-eslint/eslint-plugin-react#3834

7.37.0 - 2024.09.26

Added

Changed

#3831: jsx-eslint/eslint-plugin-react#3831 #3830: jsx-eslint/eslint-plugin-react#3830 #3826: jsx-eslint/eslint-plugin-react#3826 #3824: jsx-eslint/eslint-plugin-react#3824 #3805: jsx-eslint/eslint-plugin-react#3805

7.36.1 - 2024.09.12

Fixed

#3823: jsx-eslint/eslint-plugin-react#3823 #3821: jsx-eslint/eslint-plugin-react#3821

... (truncated)

Commits
  • 256cf74 Update CHANGELOG and bump version
  • 33db656 [Deps] update es-iterator-helpers
  • 5696f99 [Dev Deps] update @babel/core, @babel/eslint-parser, `@babel/plugin-synta...
  • 5c23573 [Dev Deps] update @babel/core, @​babel/eslint-parser, @​babel/plugin-syntax...
  • c47fa56 [types] [Fix] ensure the index types are generated
  • 63aceff [Fix] destructuring-assignment: fix false negative when using typeof props.a
  • 96d46d5 [Refactor] destructuring-assignment: use getParentStatelessComponent
  • ae6fb8d Update CHANGELOG and bump version
  • 63e0b49 [meta] do not npmignore d.ts files
  • 1f95a24 [readme] Fix shared settings link
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

@dependabot dependabot bot added dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code labels Nov 1, 2024
@github-actions github-actions bot enabled auto-merge (squash) November 1, 2024 03:06
Copy link
Contributor

github-actions bot commented Nov 1, 2024

Diff between eslint-plugin-react 7.35.0 and 7.37.2
diff --git a/lib/util/annotations.js b/lib/util/annotations.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/annotations.js
+++ b/lib/util/annotations.js
@@ -13,5 +13,5 @@
  * @param {ASTNode} node The AST node being checked.
  * @param {Object} context
- * @returns {Boolean} True if the node is a type annotated props declaration, false if not.
+ * @returns {boolean} True if the node is a type annotated props declaration, false if not.
  */
 function isAnnotatedFunctionPropsDeclaration(node, context) {
diff --git a/lib/util/ast.js b/lib/util/ast.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/ast.js
+++ b/lib/util/ast.js
@@ -93,5 +93,5 @@
 
   /* TODO: properly warn on React.forwardRefs having typo properties
-  if (nodeType === 'CallExpression') {
+  if (astUtil.isCallExpression(ASTNode)) {
     const callee = ASTNode.callee;
     const pragma = pragmaUtil.getFromContext(context);
@@ -149,5 +149,9 @@
  */
 function getPropertyNameNode(node) {
-  if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
+  if (
+    node.key
+    || node.type === 'MethodDefinition'
+    || node.type === 'Property'
+  ) {
     return node.key;
   }
@@ -161,5 +165,5 @@
  * Get properties name
  * @param {Object} node - Property.
- * @returns {String} Property name.
+ * @returns {string} Property name.
  */
 function getPropertyName(node) {
@@ -211,5 +215,5 @@
  * @param {Object} context The node to check
  * @param {ASTNode} node The node to check
- * @return {Boolean} true if it's the first node in its line
+ * @return {boolean} true if it's the first node in its line
  */
 function isNodeFirstInLine(context, node) {
@@ -223,5 +227,5 @@
  * Checks if the node is a function or arrow function expression.
  * @param {ASTNode} node The node to check
- * @return {Boolean} true if it's a function-like expression
+ * @return {boolean} true if it's a function-like expression
  */
 function isFunctionLikeExpression(node) {
@@ -232,5 +236,5 @@
  * Checks if the node is a function.
  * @param {ASTNode} node The node to check
- * @return {Boolean} true if it's a function
+ * @return {boolean} true if it's a function
  */
 function isFunction(node) {
@@ -241,5 +245,5 @@
  * Checks if node is a function declaration or expression or arrow function.
  * @param {ASTNode} node The node to check
- * @return {Boolean} true if it's a function-like
+ * @return {boolean} true if it's a function-like
  */
 function isFunctionLike(node) {
@@ -250,5 +254,5 @@
  * Checks if the node is a class.
  * @param {ASTNode} node The node to check
- * @return {Boolean} true if it's a class
+ * @return {boolean} true if it's a class
  */
 function isClass(node) {
@@ -330,5 +334,5 @@
  * Checks if a node is being assigned a value: props.bar = 'bar'
  * @param {ASTNode} node The AST node being checked.
- * @returns {Boolean}
+ * @returns {boolean}
  */
 function isAssignmentLHS(node) {
@@ -340,5 +344,18 @@
 }
 
+function isTSAsExpression(node) {
+  return node && node.type === 'TSAsExpression';
+}
+
 /**
+ * Matcher used to check whether given node is a `CallExpression`
+ * @param {ASTNode} node The AST node
+ * @returns {boolean} True if node is a `CallExpression`, false if not
+ */
+function isCallExpression(node) {
+  return node && node.type === 'CallExpression';
+}
+
+/**
  * Extracts the expression node that is wrapped inside a TS type assertion
  *
@@ -347,121 +364,120 @@
  */
 function unwrapTSAsExpression(node) {
-  if (node && node.type === 'TSAsExpression') return node.expression;
-  return node;
+  return isTSAsExpression(node) ? node.expression : node;
 }
 
 function isTSTypeReference(node) {
   if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSTypeReference';
+
+  return node.type === 'TSTypeReference';
 }
 
 function isTSTypeAnnotation(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSTypeAnnotation';
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeAnnotation';
 }
 
 function isTSTypeLiteral(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSTypeLiteral';
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeLiteral';
 }
 
 function isTSIntersectionType(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSIntersectionType';
+  if (!node) { return false; }
+
+  return node.type === 'TSIntersectionType';
 }
 
 function isTSInterfaceHeritage(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSInterfaceHeritage';
+  if (!node) { return false; }
+
+  return node.type === 'TSInterfaceHeritage';
 }
 
 function isTSInterfaceDeclaration(node) {
-  if (!node) return false;
-  let nodeType = node.type;
-  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
-    nodeType = node.declaration.type;
-  }
-  return nodeType === 'TSInterfaceDeclaration';
+  if (!node) { return false; }
+
+  return (node.type === 'ExportNamedDeclaration' && node.declaration
+    ? node.declaration.type
+    : node.type
+  ) === 'TSInterfaceDeclaration';
 }
 
 function isTSTypeDeclaration(node) {
-  if (!node) return false;
-  let nodeType = node.type;
-  let nodeKind = node.kind;
-  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
-    nodeType = node.declaration.type;
-    nodeKind = node.declaration.kind;
-  }
-  return nodeType === 'VariableDeclaration' && nodeKind === 'type';
+  if (!node) { return false; }
+
+  const nodeToCheck = node.type === 'ExportNamedDeclaration' && node.declaration
+    ? node.declaration
+    : node;
+
+  return nodeToCheck.type === 'VariableDeclaration' && nodeToCheck.kind === 'type';
 }
 
 function isTSTypeAliasDeclaration(node) {
-  if (!node) return false;
-  let nodeType = node.type;
+  if (!node) { return false; }
+
   if (node.type === 'ExportNamedDeclaration' && node.declaration) {
-    nodeType = node.declaration.type;
-    return nodeType === 'TSTypeAliasDeclaration' && node.exportKind === 'type';
+    return node.declaration.type === 'TSTypeAliasDeclaration' && node.exportKind === 'type';
   }
-  return nodeType === 'TSTypeAliasDeclaration';
+  return node.type === 'TSTypeAliasDeclaration';
 }
 
 function isTSParenthesizedType(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSTypeAliasDeclaration';
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeAliasDeclaration';
 }
 
 function isTSFunctionType(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSFunctionType';
+  if (!node) { return false; }
+
+  return node.type === 'TSFunctionType';
 }
 
 function isTSTypeQuery(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSTypeQuery';
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeQuery';
 }
 
 function isTSTypeParameterInstantiation(node) {
-  if (!node) return false;
-  const nodeType = node.type;
-  return nodeType === 'TSTypeParameterInstantiation';
+  if (!node) { return false; }
+
+  return node.type === 'TSTypeParameterInstantiation';
 }
 
 module.exports = {
-  traverse,
   findReturnStatement,
+  getComponentProperties,
   getFirstNodeInLine,
+  getKeyValue,
   getPropertyName,
   getPropertyNameNode,
-  getComponentProperties,
-  getKeyValue,
-  isParenthesized,
+  inConstructor,
   isAssignmentLHS,
+  isCallExpression,
   isClass,
   isFunction,
+  isFunctionLike,
   isFunctionLikeExpression,
-  isFunctionLike,
-  inConstructor,
   isNodeFirstInLine,
-  unwrapTSAsExpression,
-  traverseReturns,
-  isTSTypeReference,
+  isParenthesized,
+  isTSAsExpression,
+  isTSFunctionType,
+  isTSInterfaceDeclaration,
+  isTSInterfaceHeritage,
+  isTSIntersectionType,
+  isTSParenthesizedType,
+  isTSTypeAliasDeclaration,
   isTSTypeAnnotation,
+  isTSTypeDeclaration,
   isTSTypeLiteral,
-  isTSIntersectionType,
-  isTSInterfaceHeritage,
-  isTSInterfaceDeclaration,
-  isTSTypeAliasDeclaration,
-  isTSParenthesizedType,
-  isTSFunctionType,
+  isTSTypeParameterInstantiation,
   isTSTypeQuery,
-  isTSTypeParameterInstantiation,
-  isTSTypeDeclaration,
+  isTSTypeReference,
+  traverse,
+  traverseReturns,
+  unwrapTSAsExpression,
 };
diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/boolean-prop-naming.js
+++ b/lib/rules/boolean-prop-naming.js
@@ -11,4 +11,5 @@
 const Components = require('../util/Components');
 const propsUtil = require('../util/props');
+const astUtil = require('../util/ast');
 const docsUrl = require('../util/docsUrl');
 const propWrapperUtil = require('../util/propWrapper');
@@ -19,4 +20,16 @@
 const getText = eslintUtil.getText;
 
+/**
+ * Checks if prop is nested
+ * @param {Object} prop Property object, single prop type declaration
+ * @returns {boolean}
+ */
+function nestedPropTypes(prop) {
+  return (
+    prop.type === 'Property'
+    && astUtil.isCallExpression(prop.value)
+  );
+}
+
 // ------------------------------------------------------------------------------
 // Rule Definition
@@ -129,5 +142,5 @@
      * Checks if prop is declared in flow way
      * @param {Object} prop Property object, single prop type declaration
-     * @returns {Boolean}
+     * @returns {boolean}
      */
     function flowCheck(prop) {
@@ -142,5 +155,5 @@
      * Checks if prop is declared in regular way
      * @param {Object} prop Property object, single prop type declaration
-     * @returns {Boolean}
+     * @returns {boolean}
      */
     function regularCheck(prop) {
@@ -164,16 +177,4 @@
 
     /**
-     * Checks if prop is nested
-     * @param {Object} prop Property object, single prop type declaration
-     * @returns {Boolean}
-     */
-    function nestedPropTypes(prop) {
-      return (
-        prop.type === 'Property'
-        && prop.value.type === 'CallExpression'
-      );
-    }
-
-    /**
      * Runs recursive check on all proptypes
      * @param {Array} proptypes A list of Property object (for each proptype defined)
@@ -181,13 +182,15 @@
      */
     function runCheck(proptypes, addInvalidProp) {
-      (proptypes || []).forEach((prop) => {
-        if (config.validateNested && nestedPropTypes(prop)) {
-          runCheck(prop.value.arguments[0].properties, addInvalidProp);
-          return;
-        }
-        if (flowCheck(prop) || regularCheck(prop) || tsCheck(prop)) {
-          addInvalidProp(prop);
-        }
-      });
+      if (proptypes) {
+        proptypes.forEach((prop) => {
+          if (config.validateNested && nestedPropTypes(prop)) {
+            runCheck(prop.value.arguments[0].properties, addInvalidProp);
+            return;
+          }
+          if (flowCheck(prop) || regularCheck(prop) || tsCheck(prop)) {
+            addInvalidProp(prop);
+          }
+        });
+      }
     }
 
@@ -257,12 +260,14 @@
       }
 
-      const annotationTypeParams = component.node.parent.id.typeAnnotation.typeAnnotation.typeParameters;
+      const annotationTypeArguments = propsUtil.getTypeArguments(
+        component.node.parent.id.typeAnnotation.typeAnnotation
+      );
       if (
-        annotationTypeParams && (
-          annotationTypeParams.type === 'TSTypeParameterInstantiation'
-          || annotationTypeParams.type === 'TypeParameterInstantiation'
+        annotationTypeArguments && (
+          annotationTypeArguments.type === 'TSTypeParameterInstantiation'
+          || annotationTypeArguments.type === 'TypeParameterInstantiation'
         )
       ) {
-        return annotationTypeParams.params.find(
+        return annotationTypeArguments.params.find(
           (param) => param.type === 'TSTypeReference' || param.type === 'GenericTypeAnnotation'
         );
@@ -310,5 +315,5 @@
         if (
           node.value
-          && node.value.type === 'CallExpression'
+          && astUtil.isCallExpression(node.value)
           && propWrapperUtil.isPropWrapperFunction(
             context,
@@ -336,5 +341,5 @@
         const right = node.parent.right;
         if (
-          right.type === 'CallExpression'
+          astUtil.isCallExpression(right)
           && propWrapperUtil.isPropWrapperFunction(
             context,
diff --git a/lib/rules/button-has-type.js b/lib/rules/button-has-type.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/button-has-type.js
+++ b/lib/rules/button-has-type.js
@@ -29,4 +29,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -150,5 +151,10 @@
 
         const props = node.arguments[1].properties;
-        const typeProp = props.find((prop) => prop.key && prop.key.name === 'type');
+        const typeProp = props.find((prop) => (
+          'key' in prop
+          && prop.key
+          && 'name' in prop.key
+          && prop.key.name === 'type'
+        ));
 
         if (!typeProp) {
@@ -157,5 +163,5 @@
         }
 
-        checkExpression(node, typeProp.value);
+        checkExpression(node, 'value' in typeProp ? typeProp.value : undefined);
       },
     };
diff --git a/lib/rules/checked-requires-onchange-or-readonly.js b/lib/rules/checked-requires-onchange-or-readonly.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/checked-requires-onchange-or-readonly.js
+++ b/lib/rules/checked-requires-onchange-or-readonly.js
@@ -25,5 +25,5 @@
 
 /**
- * @param {string[]} properties
+ * @param {object[]} properties
  * @param {string} keyName
  * @returns {Set<string>}
@@ -42,4 +42,5 @@
 }
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
diff --git a/lib/util/Components.js b/lib/util/Components.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/Components.js
+++ b/lib/util/Components.js
@@ -71,5 +71,5 @@
    *
    * @param {ASTNode} node The AST node being added.
-   * @param {Number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes)
+   * @param {number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes)
    * @returns {Object} Added component object
    */
@@ -184,5 +184,5 @@
    * Components for which we are not confident are not counted
    *
-   * @returns {Number} Components list length
+   * @returns {number} Components list length
    */
   length() {
@@ -304,18 +304,18 @@
 
     /**
-     * @param {ASTNode} ASTNode
+     * @param {ASTNode} node
      * @param {boolean=} strict
      * @returns {boolean}
      */
-    isReturningJSX(ASTNode, strict) {
-      return jsxUtil.isReturningJSX(context, ASTNode, strict, true);
+    isReturningJSX(node, strict) {
+      return jsxUtil.isReturningJSX(context, node, strict, true);
     },
 
-    isReturningJSXOrNull(ASTNode, strict) {
-      return jsxUtil.isReturningJSX(context, ASTNode, strict);
+    isReturningJSXOrNull(node, strict) {
+      return jsxUtil.isReturningJSX(context, node, strict);
     },
 
-    isReturningOnlyNull(ASTNode) {
-      return jsxUtil.isReturningOnlyNull(ASTNode, context);
+    isReturningOnlyNull(node) {
+      return jsxUtil.isReturningOnlyNull(node, context);
     },
 
@@ -408,5 +408,5 @@
 
     isPragmaComponentWrapper(node) {
-      if (!node || node.type !== 'CallExpression') {
+      if (!astUtil.isCallExpression(node)) {
         return false;
       }
@@ -568,5 +568,11 @@
 
         // for case abc = { [someobject.somekey]: props => { ... return not-jsx } }
-        if (node.parent && node.parent.key && node.parent.key.type === 'MemberExpression' && !utils.isReturningJSX(node) && !utils.isReturningOnlyNull(node)) {
+        if (
+          node.parent
+          && node.parent.key
+          && node.parent.key.type === 'MemberExpression'
+          && !utils.isReturningJSX(node)
+          && !utils.isReturningOnlyNull(node)
+        ) {
           return undefined;
         }
@@ -578,5 +584,8 @@
           )
         ) {
-          if (isFirstLetterCapitalized(node.parent.key.name) && utils.isReturningJSX(node)) {
+          if (
+            isFirstLetterCapitalized(node.parent.key.name)
+            && utils.isReturningJSX(node)
+          ) {
             return node;
           }
@@ -642,5 +651,5 @@
      *
      * @param {ASTNode} node The AST node being checked (must be a MemberExpression).
-     * @returns {ASTNode} component node, null if we cannot find the component
+     * @returns {ASTNode | null} component node, null if we cannot find the component
      */
     getRelatedComponent(node) {
@@ -752,8 +761,8 @@
      * @param {ASTNode} node The AST node being searched. (expects CallExpression)
      * @param {('useCallback'|'useContext'|'useDebugValue'|'useEffect'|'useImperativeHandle'|'useLayoutEffect'|'useMemo'|'useReducer'|'useRef'|'useState')[]} [expectedHookNames] React hook names to which search is limited.
-     * @returns {Boolean} True if the node is a call to a React hook
+     * @returns {boolean} True if the node is a call to a React hook
      */
     isReactHookCall(node, expectedHookNames) {
-      if (node.type !== 'CallExpression') {
+      if (!astUtil.isCallExpression(node)) {
         return false;
       }
@@ -799,6 +808,12 @@
 
       const hookResolvedDefs = potentialHookReference && potentialHookReference.resolved.defs;
-      const localHookName = (isPotentialReactHookCall && node.callee.property.name)
-        || (isPotentialHookCall && potentialHookReference && node.callee.name);
+      const localHookName = (
+        isPotentialReactHookCall
+        && node.callee.property.name
+      ) || (
+        isPotentialHookCall
+        && potentialHookReference
+        && node.callee.name
+      );
       const isHookShadowed = isPotentialHookCall
         && hookResolvedDefs
@@ -877,9 +892,9 @@
       }
 
-      node = utils.getStatelessComponent(node);
-      if (!node) {
+      const cNode = utils.getStatelessComponent(node);
+      if (!cNode) {
         return;
       }
-      components.add(node, 2);
+      components.add(cNode, 2);
     },
 
diff --git a/lib/util/componentUtil.js b/lib/util/componentUtil.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/componentUtil.js
+++ b/lib/util/componentUtil.js
@@ -175,5 +175,7 @@
  */
 function isStateMemberExpression(node) {
-  return node.type === 'MemberExpression' && node.object.type === 'ThisExpression' && node.property.name === 'state';
+  return node.type === 'MemberExpression'
+    && node.object.type === 'ThisExpression'
+    && node.property.name === 'state';
 }
 
diff --git a/lib/util/defaultProps.js b/lib/util/defaultProps.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/defaultProps.js
+++ b/lib/util/defaultProps.js
@@ -27,5 +27,5 @@
     }
     if (
-      node.type === 'CallExpression'
+      astUtil.isCallExpression(node)
       && propWrapperUtil.isPropWrapperFunction(context, node.callee.name)
       && node.arguments && node.arguments[0]
diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/destructuring-assignment.js
+++ b/lib/rules/destructuring-assignment.js
@@ -182,4 +182,23 @@
     }
 
+    // valid-jsdoc cannot read function types
+    // eslint-disable-next-line valid-jsdoc
+    /**
+     * Find a parent that satisfy the given predicate
+     * @param {ASTNode} node
+     * @param {(node: ASTNode) => boolean} predicate
+     * @returns {ASTNode | undefined}
+     */
+    function findParent(node, predicate) {
+      let n = node;
+      while (n) {
+        if (predicate(n)) {
+          return n;
+        }
+        n = n.parent;
+      }
+      return undefined;
+    }
+
     return {
 
@@ -197,10 +216,5 @@
 
       MemberExpression(node) {
-        let scope = getScope(context, node);
-        let SFCComponent = components.get(scope.block);
-        while (!SFCComponent && scope.upper && scope.upper !== scope) {
-          SFCComponent = components.get(scope.upper.block);
-          scope = scope.upper;
-        }
+        const SFCComponent = utils.getParentStatelessComponent(node);
         if (SFCComponent) {
           handleSFCUsage(node);
@@ -213,4 +227,23 @@
       },
 
+      TSQualifiedName(node) {
+        if (configuration !== 'always') {
+          return;
+        }
+        // handle `typeof props.a.b`
+        if (node.left.type === 'Identifier'
+          && node.left.name === sfcParams.propsName()
+          && findParent(node, (n) => n.type === 'TSTypeQuery')
+          && utils.getParentStatelessComponent(node)
+        ) {
+          report(context, messages.useDestructAssignment, 'useDestructAssignment', {
+            node,
+            data: {
+              type: 'props',
+            },
+          });
+        }
+      },
+
       VariableDeclarator(node) {
         const classComponent = utils.getParentComponent(node);
@@ -258,4 +291,5 @@
             return;
           }
+
           // Skip if props is used elsewhere
           if (propsRefs.length > 1) {
diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/display-name.js
+++ b/lib/rules/display-name.js
@@ -74,10 +74,11 @@
      * Checks if React.forwardRef is nested inside React.memo
      * @param {ASTNode} node The AST node being checked.
-     * @returns {Boolean} True if React.forwardRef is nested inside React.memo, false if not.
+     * @returns {boolean} True if React.forwardRef is nested inside React.memo, false if not.
      */
     function isNestedMemo(node) {
-      const argumentIsCallExpression = node.arguments && node.arguments[0] && node.arguments[0].type === 'CallExpression';
-
-      return node.type === 'CallExpression' && argumentIsCallExpression && utils.isPragmaComponentWrapper(node);
+      return astUtil.isCallExpression(node)
+        && node.arguments
+        && astUtil.isCallExpression(node.arguments[0])
+        && utils.isPragmaComponentWrapper(node);
     }
 
@@ -112,5 +113,5 @@
      * Checks if the component have a name set by the transpiler
      * @param {ASTNode} node The AST node being checked.
-     * @returns {Boolean} True if component has a name, false if not.
+     * @returns {boolean} True if component has a name, false if not.
      */
     function hasTranspilerName(node) {
@@ -199,5 +200,5 @@
           return;
         }
-        markDisplayNameAsDeclared(component.node.type === 'TSAsExpression' ? component.node.expression : component.node);
+        markDisplayNameAsDeclared(astUtil.unwrapTSAsExpression(component.node));
       },
 
diff --git a/lib/util/error.js b/lib/util/error.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/error.js
+++ b/lib/util/error.js
@@ -3,5 +3,5 @@
 /**
  * Logs out a message if there is no format option set.
- * @param {String} message - Message to log.
+ * @param {string} message - Message to log.
  */
 function error(message) {
diff --git a/lib/rules/forbid-component-props.js b/lib/rules/forbid-component-props.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/forbid-component-props.js
+++ b/lib/rules/forbid-component-props.js
@@ -53,4 +53,9 @@
                     items: { type: 'string' },
                   },
+                  allowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    items: { type: 'string' },
+                  },
                   message: { type: 'string' },
                 },
@@ -67,10 +72,18 @@
                     items: { type: 'string' },
                   },
+                  disallowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    minItems: 1,
+                    items: { type: 'string' },
+                  },
                   message: { type: 'string' },
                 },
-                required: ['disallowedFor'],
+                anyOf: [
+                  { required: ['disallowedFor'] },
+                  { required: ['disallowedForPatterns'] },
+                ],
                 additionalProperties: false,
               },
-
               {
                 type: 'object',
@@ -82,4 +95,9 @@
                     items: { type: 'string' },
                   },
+                  allowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    items: { type: 'string' },
+                  },
                   message: { type: 'string' },
                 },
@@ -96,7 +114,16 @@
                     items: { type: 'string' },
                   },
+                  disallowedForPatterns: {
+                    type: 'array',
+                    uniqueItems: true,
+                    minItems: 1,
+                    items: { type: 'string' },
+                  },
                   message: { type: 'string' },
                 },
-                required: ['disallowedFor'],
+                anyOf: [
+                  { required: ['disallowedFor'] },
+                  { required: ['disallowedForPatterns'] },
+                ],
                 additionalProperties: false,
               },
@@ -115,6 +142,8 @@
       const prop = propName || propPattern;
       const options = {
-        allowList: typeof value === 'string' ? [] : (value.allowedFor || []),
-        disallowList: typeof value === 'string' ? [] : (value.disallowedFor || []),
+        allowList: [].concat(value.allowedFor || []),
+        allowPatternList: [].concat(value.allowedForPatterns || []),
+        disallowList: [].concat(value.disallowedFor || []),
+        disallowPatternList: [].concat(value.disallowedForPatterns || []),
         message: typeof value === 'string' ? null : value.message,
         isPattern: !!value.propNamePattern,
@@ -141,8 +170,38 @@
       }
 
+      function checkIsTagForbiddenByAllowOptions() {
+        if (options.allowList.indexOf(tagName) !== -1) {
+          return false;
+        }
+
+        if (options.allowPatternList.length === 0) {
+          return true;
+        }
+
+        return options.allowPatternList.every(
+          (pattern) => !minimatch(tagName, pattern)
+        );
+      }
+
+      function checkIsTagForbiddenByDisallowOptions() {
+        if (options.disallowList.indexOf(tagName) !== -1) {
+          return true;
+        }
+
+        if (options.disallowPatternList.length === 0) {
+          return false;
+        }
+
+        return options.disallowPatternList.some(
+          (pattern) => minimatch(tagName, pattern)
+        );
+      }
+
+      const hasDisallowOptions = options.disallowList.length > 0 || options.disallowPatternList.length > 0;
+
       // disallowList should have a least one item (schema configuration)
-      const isTagForbidden = options.disallowList.length > 0
-        ? options.disallowList.indexOf(tagName) !== -1
-        : options.allowList.indexOf(tagName) === -1;
+      const isTagForbidden = hasDisallowOptions
+        ? checkIsTagForbiddenByDisallowOptions()
+        : checkIsTagForbiddenByAllowOptions();
 
       // if the tagName is undefined (`<this.something>`), we assume it's a forbidden element
diff --git a/lib/rules/forbid-elements.js b/lib/rules/forbid-elements.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/forbid-elements.js
+++ b/lib/rules/forbid-elements.js
@@ -21,4 +21,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -106,11 +107,9 @@
         }
 
-        const argType = argument.type;
-
-        if (argType === 'Identifier' && /^[A-Z_]/.test(argument.name)) {
+        if (argument.type === 'Identifier' && /^[A-Z_]/.test(argument.name)) {
           reportIfForbidden(argument.name, argument);
-        } else if (argType === 'Literal' && /^[a-z][^.]*$/.test(argument.value)) {
+        } else if (argument.type === 'Literal' && /^[a-z][^.]*$/.test(String(argument.value))) {
           reportIfForbidden(argument.value, argument);
-        } else if (argType === 'MemberExpression') {
+        } else if (argument.type === 'MemberExpression') {
           reportIfForbidden(getText(context, argument), argument);
         }
diff --git a/lib/rules/forbid-foreign-prop-types.js b/lib/rules/forbid-foreign-prop-types.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/forbid-foreign-prop-types.js
+++ b/lib/rules/forbid-foreign-prop-types.js
@@ -14,4 +14,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -109,5 +110,7 @@
             && !isAllowedAssignment(node)
           )) || (
+            // @ts-expect-error The JSXText type is not present in the estree type definitions
             (node.property.type === 'Literal' || node.property.type === 'JSXText')
+            && 'value' in node.property
             && node.property.value === 'propTypes'
             && !ast.isAssignmentLHS(node)
@@ -122,5 +125,9 @@
 
       ObjectPattern(node) {
-        const propTypesNode = node.properties.find((property) => property.type === 'Property' && property.key.name === 'propTypes');
+        const propTypesNode = node.properties.find((property) => (
+          property.type === 'Property'
+          && 'name' in property.key
+          && property.key.name === 'propTypes'
+        ));
 
         if (propTypesNode) {
diff --git a/lib/rules/forbid-prop-types.js b/lib/rules/forbid-prop-types.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/forbid-prop-types.js
+++ b/lib/rules/forbid-prop-types.js
@@ -27,4 +27,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -135,5 +136,5 @@
             value = value.object;
           }
-          if (value.type === 'CallExpression') {
+          if (astUtil.isCallExpression(value)) {
             if (!isPropTypesPackage(value.callee)) {
               return;
@@ -159,27 +160,23 @@
 
     function checkNode(node) {
-      switch (node && node.type) {
-        case 'ObjectExpression':
-          checkProperties(node.properties);
-          break;
-        case 'Identifier': {
-          const propTypesObject = variableUtil.findVariableByName(context, node, node.name);
-          if (propTypesObject && propTypesObject.properties) {
-            checkProperties(propTypesObject.properties);
-          }
-          break;
+      if (!node) {
+        return;
+      }
+
+      if (node.type === 'ObjectExpression') {
+        checkProperties(node.properties);
+      } else if (node.type === 'Identifier') {
+        const propTypesObject = variableUtil.findVariableByName(context, node, node.name);
+        if (propTypesObject && propTypesObject.properties) {
+          checkProperties(propTypesObject.properties);
         }
-        case 'CallExpression': {
-          const innerNode = node.arguments && node.arguments[0];
-          if (
-            propWrapperUtil.isPropWrapperFunction(context, getText(context, node.callee))
+      } else if (astUtil.isCallExpression(node)) {
+        const innerNode = node.arguments && node.arguments[0];
+        if (
+          propWrapperUtil.isPropWrapperFunction(context, getText(context, node.callee))
             && innerNode
-          ) {
-            checkNode(innerNode);
-          }
-          break;
+        ) {
+          checkNode(innerNode);
         }
-        default:
-          break;
       }
     }
@@ -197,5 +194,7 @@
           if (node.specifiers.length >= 1) {
             const propTypesSpecifier = node.specifiers.find((specifier) => (
-              specifier.imported && specifier.imported.name === 'PropTypes'
+              'imported' in specifier
+              && specifier.imported
+              && specifier.imported.name === 'PropTypes'
             ));
             if (propTypesSpecifier) {
@@ -233,10 +232,11 @@
         }
 
-        checkNode(node.parent.right);
+        checkNode('right' in node.parent && node.parent.right);
       },
 
       CallExpression(node) {
         if (
-          node.callee.object
+          node.callee.type === 'MemberExpression'
+          && node.callee.object
           && !isPropTypesPackage(node.callee.object)
           && !propsUtil.isPropTypesDeclaration(node.callee)
@@ -247,7 +247,10 @@
         if (
           node.arguments.length > 0
-          && (node.callee.name === 'shape' || astUtil.getPropertyName(node.callee) === 'shape')
+          && (
+            ('name' in node.callee && node.callee.name === 'shape')
+            || astUtil.getPropertyName(node.callee) === 'shape'
+          )
         ) {
-          checkProperties(node.arguments[0].properties);
+          checkProperties('properties' in node.arguments[0] && node.arguments[0].properties);
         }
       },
@@ -272,5 +275,5 @@
       ObjectExpression(node) {
         node.properties.forEach((property) => {
-          if (!property.key) {
+          if (!('key' in property) || !property.key) {
             return;
           }
diff --git a/lib/rules/function-component-definition.js b/lib/rules/function-component-definition.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/function-component-definition.js
+++ b/lib/rules/function-component-definition.js
@@ -11,4 +11,5 @@
 const reportC = require('../util/report');
 const getText = require('../util/eslint').getText;
+const propsUtil = require('../util/props');
 
 // ------------------------------------------------------------------------------
@@ -35,10 +36,10 @@
 
 function hasOneUnconstrainedTypeParam(node) {
-  const nodeTypeParams = node.typeParameters;
+  const nodeTypeArguments = propsUtil.getTypeArguments(node);
 
-  return nodeTypeParams
-    && nodeTypeParams.params
-    && nodeTypeParams.params.length === 1
-    && !nodeTypeParams.params[0].constraint;
+  return nodeTypeArguments
+    && nodeTypeArguments.params
+    && nodeTypeArguments.params.length === 1
+    && !nodeTypeArguments.params[0].constraint;
 }
 
@@ -203,9 +204,10 @@
       }
 
+      const nodeTypeArguments = propsUtil.getTypeArguments(node);
       return (fixer) => fixer.replaceTextRange(
         options.range,
         buildFunction(options.template, {
           typeAnnotation,
-          typeParams: getNodeText(node.typeParameters, source),
+          typeParams: getNodeText(nodeTypeArguments, source),
           params: getParams(node, source),
           returnType: getNodeText(node.returnType, source),
diff --git a/lib/rules/hook-use-state.js b/lib/rules/hook-use-state.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/hook-use-state.js
+++ b/lib/rules/hook-use-state.js
@@ -27,4 +27,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
diff --git a/index.js b/index.js
index v7.35.0..v7.37.2 100644
--- a/index.js
+++ b/index.js
@@ -12,5 +12,5 @@
 /**
  * @param {object} rules - rules object mapping rule name to rule module
- * @returns {Record<string, 2>}
+ * @returns {Record<string, 2 | 'error'>}
  */
 function configureAsError(rules) {
@@ -18,9 +18,13 @@
 }
 
+/** @type {Partial<typeof allRules>} */
 const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated);
+/** @type {Record<keyof typeof activeRules, 2 | 'error'>} */
 const activeRulesConfig = configureAsError(activeRules);
 
+/** @type {Partial<typeof allRules>} */
 const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated);
 
+/** @type {['react']} */
 // for legacy config system
 const plugins = [
@@ -28,80 +32,86 @@
 ];
 
-const plugin = {
-  deprecatedRules,
-  rules: allRules,
-  configs: {
-    recommended: {
-      plugins,
-      parserOptions: {
-        ecmaFeatures: {
-          jsx: true,
-        },
+const configs = {
+  recommended: {
+    plugins,
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
       },
-      rules: {
-        'react/display-name': 2,
-        'react/jsx-key': 2,
-        'react/jsx-no-comment-textnodes': 2,
-        'react/jsx-no-duplicate-props': 2,
-        'react/jsx-no-target-blank': 2,
-        'react/jsx-no-undef': 2,
-        'react/jsx-uses-react': 2,
-        'react/jsx-uses-vars': 2,
-        'react/no-children-prop': 2,
-        'react/no-danger-with-children': 2,
-        'react/no-deprecated': 2,
-        'react/no-direct-mutation-state': 2,
-        'react/no-find-dom-node': 2,
-        'react/no-is-mounted': 2,
-        'react/no-render-return-value': 2,
-        'react/no-string-refs': 2,
-        'react/no-unescaped-entities': 2,
-        'react/no-unknown-property': 2,
-        'react/no-unsafe': 0,
-        'react/prop-types': 2,
-        'react/react-in-jsx-scope': 2,
-        'react/require-render-return': 2,
-      },
     },
-    all: {
-      plugins,
-      parserOptions: {
-        ecmaFeatures: {
-          jsx: true,
-        },
+    rules: {
+      'react/display-name': 2,
+      'react/jsx-key': 2,
+      'react/jsx-no-comment-textnodes': 2,
+      'react/jsx-no-duplicate-props': 2,
+      'react/jsx-no-target-blank': 2,
+      'react/jsx-no-undef': 2,
+      'react/jsx-uses-react': 2,
+      'react/jsx-uses-vars': 2,
+      'react/no-children-prop': 2,
+      'react/no-danger-with-children': 2,
+      'react/no-deprecated': 2,
+      'react/no-direct-mutation-state': 2,
+      'react/no-find-dom-node': 2,
+      'react/no-is-mounted': 2,
+      'react/no-render-return-value': 2,
+      'react/no-string-refs': 2,
+      'react/no-unescaped-entities': 2,
+      'react/no-unknown-property': 2,
+      'react/no-unsafe': 0,
+      'react/prop-types': 2,
+      'react/react-in-jsx-scope': 2,
+      'react/require-render-return': 2,
+    },
+  },
+  all: {
+    plugins,
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
       },
-      rules: activeRulesConfig,
     },
-    'jsx-runtime': {
-      plugins,
-      parserOptions: {
-        ecmaFeatures: {
-          jsx: true,
-        },
-        jsxPragma: null, // for @typescript/eslint-parser
+    rules: activeRulesConfig,
+  },
+  'jsx-runtime': {
+    plugins,
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
       },
-      rules: {
-        'react/react-in-jsx-scope': 0,
-        'react/jsx-uses-react': 0,
-      },
+      jsxPragma: null, // for @typescript/eslint-parser
     },
+    rules: {
+      'react/react-in-jsx-scope': 0,
+      'react/jsx-uses-react': 0,
+    },
   },
 };
 
-plugin.configs.flat = {
+/** @typedef {{ plugins: { react: typeof plugin }, rules: import('eslint').Linter.RulesRecord, languageOptions: { parserOptions: import('eslint').Linter.ParserOptions } }} ReactFlatConfig */
+
+/** @type {{ deprecatedRules: typeof deprecatedRules, rules: typeof allRules, configs: typeof configs & { flat?: Record<string, ReactFlatConfig> }}} */
+const plugin = {
+  deprecatedRules,
+  rules: allRules,
+  configs,
+};
+
+/** @type {Record<string, ReactFlatConfig>} */
+configs.flat = {
   recommended: {
     plugins: { react: plugin },
-    rules: plugin.configs.recommended.rules,
-    languageOptions: { parserOptions: plugin.configs.recommended.parserOptions },
+    rules: configs.recommended.rules,
+    languageOptions: { parserOptions: configs.recommended.parserOptions },
   },
   all: {
     plugins: { react: plugin },
-    rules: plugin.configs.all.rules,
-    languageOptions: { parserOptions: plugin.configs.all.parserOptions },
+    rules: configs.all.rules,
+    languageOptions: { parserOptions: configs.all.parserOptions },
   },
   'jsx-runtime': {
     plugins: { react: plugin },
-    rules: plugin.configs['jsx-runtime'].rules,
-    languageOptions: { parserOptions: plugin.configs['jsx-runtime'].parserOptions },
+    rules: configs['jsx-runtime'].rules,
+    languageOptions: { parserOptions: configs['jsx-runtime'].parserOptions },
   },
 };
diff --git a/lib/rules/index.js b/lib/rules/index.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/index.js
+++ b/lib/rules/index.js
@@ -16,4 +16,5 @@
   'forbid-foreign-prop-types': require('./forbid-foreign-prop-types'),
   'forbid-prop-types': require('./forbid-prop-types'),
+  'forward-ref-uses-ref': require('./forward-ref-uses-ref'),
   'function-component-definition': require('./function-component-definition'),
   'hook-use-state': require('./hook-use-state'),
diff --git a/lib/util/isCreateContext.js b/lib/util/isCreateContext.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/isCreateContext.js
+++ b/lib/util/isCreateContext.js
@@ -1,27 +1,30 @@
 'use strict';
 
+const astUtil = require('./ast');
+
 /**
  * Checks if the node is a React.createContext call
  * @param {ASTNode} node - The AST node being checked.
- * @returns {Boolean} - True if node is a React.createContext call, false if not.
+ * @returns {boolean} - True if node is a React.createContext call, false if not.
  */
 module.exports = function isCreateContext(node) {
   if (
     node.init
-    && node.init.type === 'CallExpression'
     && node.init.callee
-    && node.init.callee.name === 'createContext'
   ) {
-    return true;
-  }
+    if (
+      astUtil.isCallExpression(node.init)
+      && node.init.callee.name === 'createContext'
+    ) {
+      return true;
+    }
 
-  if (
-    node.init
-    && node.init.callee
-    && node.init.callee.type === 'MemberExpression'
-    && node.init.callee.property
-    && node.init.callee.property.name === 'createContext'
-  ) {
-    return true;
+    if (
+      node.init.callee.type === 'MemberExpression'
+      && node.init.callee.property
+      && node.init.callee.property.name === 'createContext'
+    ) {
+      return true;
+    }
   }
 
@@ -30,22 +33,20 @@
     && node.expression.type === 'AssignmentExpression'
     && node.expression.operator === '='
-    && node.expression.right.type === 'CallExpression'
+    && astUtil.isCallExpression(node.expression.right)
     && node.expression.right.callee
-    && node.expression.right.callee.name === 'createContext'
   ) {
-    return true;
-  }
+    const right = node.expression.right;
 
-  if (
-    node.expression
-    && node.expression.type === 'AssignmentExpression'
-    && node.expression.operator === '='
-    && node.expression.right.type === 'CallExpression'
-    && node.expression.right.callee
-    && node.expression.right.callee.type === 'MemberExpression'
-    && node.expression.right.callee.property
-    && node.expression.right.callee.property.name === 'createContext'
-  ) {
-    return true;
+    if (right.callee.name === 'createContext') {
+      return true;
+    }
+
+    if (
+      right.callee.type === 'MemberExpression'
+      && right.callee.property
+      && right.callee.property.name === 'createContext'
+    ) {
+      return true;
+    }
   }
 
diff --git a/lib/util/isCreateElement.js b/lib/util/isCreateElement.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/isCreateElement.js
+++ b/lib/util/isCreateElement.js
@@ -11,7 +11,10 @@
 */
 module.exports = function isCreateElement(context, node) {
+  if (!node.callee) {
+    return false;
+  }
+
   if (
-    node.callee
-    && node.callee.type === 'MemberExpression'
+    node.callee.type === 'MemberExpression'
     && node.callee.property.name === 'createElement'
     && node.callee.object
@@ -22,7 +25,5 @@
 
   if (
-    node
-    && node.callee
-    && node.callee.name === 'createElement'
+    node.callee.name === 'createElement'
     && isDestructuredFromPragmaImport(context, node, 'createElement')
   ) {
diff --git a/lib/util/isDestructuredFromPragmaImport.js b/lib/util/isDestructuredFromPragmaImport.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/isDestructuredFromPragmaImport.js
+++ b/lib/util/isDestructuredFromPragmaImport.js
@@ -1,4 +1,5 @@
 'use strict';
 
+const astUtil = require('./ast');
 const pragmaUtil = require('./pragma');
 const variableUtil = require('./variable');
@@ -23,6 +24,6 @@
         if (
           latestDef.node.init.type === 'MemberExpression'
-              && latestDef.node.init.object.type === 'Identifier'
-              && latestDef.node.init.object.name === pragma
+          && latestDef.node.init.object.type === 'Identifier'
+          && latestDef.node.init.object.name === pragma
         ) {
           return true;
@@ -31,5 +32,5 @@
         if (
           latestDef.node.init.type === 'Identifier'
-              && latestDef.node.init.name === pragma
+          && latestDef.node.init.name === pragma
         ) {
           return true;
@@ -40,5 +41,5 @@
 
         // get "require('react')" from: "{variable} = require('react')"
-        if (latestDef.node.init.type === 'CallExpression') {
+        if (astUtil.isCallExpression(latestDef.node.init)) {
           requireExpression = latestDef.node.init;
         }
@@ -46,6 +47,6 @@
         if (
           !requireExpression
-              && latestDef.node.init.type === 'MemberExpression'
-              && latestDef.node.init.object.type === 'CallExpression'
+          && latestDef.node.init.type === 'MemberExpression'
+          && astUtil.isCallExpression(latestDef.node.init.object)
         ) {
           requireExpression = latestDef.node.init.object;
@@ -55,8 +56,8 @@
         if (
           requireExpression
-              && requireExpression.callee
-              && requireExpression.callee.name === 'require'
-              && requireExpression.arguments[0]
-              && requireExpression.arguments[0].value === pragma.toLocaleLowerCase()
+          && requireExpression.callee
+          && requireExpression.callee.name === 'require'
+          && requireExpression.arguments[0]
+          && requireExpression.arguments[0].value === pragma.toLocaleLowerCase()
         ) {
           return true;
@@ -69,6 +70,6 @@
       if (
         latestDef.parent
-            && latestDef.parent.type === 'ImportDeclaration'
-            && latestDef.parent.source.value === pragma.toLocaleLowerCase()
+        && latestDef.parent.type === 'ImportDeclaration'
+        && latestDef.parent.source.value === pragma.toLocaleLowerCase()
       ) {
         return true;
diff --git a/lib/util/isFirstLetterCapitalized.js b/lib/util/isFirstLetterCapitalized.js
index v7.35.0..v7.37.2 100644
--- a/lib/util/isFirstLetterCapitalized.js
+++ b/lib/util/isFirstLetterCapitalized.js
@@ -3,8 +3,8 @@
 /**
  * Check if the first letter of a string is capitalized.
- * @param {String} word String to check
- * @returns {Boolean} True if first letter is capitalized.
+ * @param {string} word String to check
+ * @returns {boolean} True if first letter is capitalized.
  */
-function isFirstLetterCapitalized(word) {
+module.exports = function isFirstLetterCapitalized(word) {
   if (!word) {
     return false;
@@ -12,5 +12,3 @@
   const firstLetter = word.replace(/^_+/, '').charAt(0);
   return firstLetter.toUpperCase() === firstLetter;
-}
-
-module.exports = isFirstLetterCapitalized;
+};
diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-closing-bracket-location.js
+++ b/lib/rules/jsx-closing-bracket-location.js
@@ -21,4 +21,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -101,5 +102,5 @@
      * Get expected location for the closing bracket
      * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
-     * @return {String} Expected location for the closing bracket
+     * @return {string} Expected location for the closing bracket
      */
     function getExpectedLocation(tokens) {
@@ -122,5 +123,5 @@
      * expected location.
      * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
-     * @param {String} expectedLocation Expected location for the closing bracket
+     * @param {string} expectedLocation Expected location for the closing bracket
      * @return {?Number} The correct column for the closing bracket, or null
      */
@@ -141,6 +142,6 @@
      * Check if the closing bracket is correctly located
      * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
-     * @param {String} expectedLocation Expected location for the closing bracket
-     * @return {Boolean} True if the closing bracket is correctly located, false if not
+     * @param {string} expectedLocation Expected location for the closing bracket
+     * @return {boolean} True if the closing bracket is correctly located, false if not
      */
     function hasCorrectLocation(tokens, expectedLocation) {
@@ -164,7 +165,7 @@
      * Get the characters used for indentation on the line to be matched
      * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
-     * @param {String} expectedLocation Expected location for the closing bracket
-     * @param {Number} [correctColumn] Expected column for the closing bracket. Default to 0
-     * @return {String} The characters used for indentation
+     * @param {string} expectedLocation Expected location for the closing bracket
+     * @param {number} [correctColumn] Expected column for the closing bracket. Default to 0
+     * @return {string} The characters used for indentation
      */
     function getIndentation(tokens, expectedLocation, correctColumn) {
@@ -236,5 +237,5 @@
      *
      * @param {ASTNode} node The AST node being checked.
-     * @returns {String} Unique ID (based on its range)
+     * @returns {string} Unique ID (based on its range)
      */
     function getOpeningElementId(node) {
diff --git a/lib/rules/jsx-curly-brace-presence.js b/lib/rules/jsx-curly-brace-presence.js
index v7.35.0..v7.37.2 100755
--- a/lib/rules/jsx-curly-brace-presence.js
+++ b/lib/rules/jsx-curly-brace-presence.js
@@ -32,4 +32,106 @@
 const DEFAULT_CONFIG = { props: OPTION_NEVER, children: OPTION_NEVER, propElementValues: OPTION_IGNORE };
 
+const HTML_ENTITY_REGEX = () => /&[A-Za-z\d#]+;/g;
+
+function containsLineTerminators(rawStringValue) {
+  return /[\n\r\u2028\u2029]/.test(rawStringValue);
+}
+
+function containsBackslash(rawStringValue) {
+  return arrayIncludes(rawStringValue, '\\');
+}
+
+function containsHTMLEntity(rawStringValue) {
+  return HTML_ENTITY_REGEX().test(rawStringValue);
+}
+
+function containsOnlyHtmlEntities(rawStringValue) {
+  return rawStringValue.replace(HTML_ENTITY_REGEX(), '').trim() === '';
+}
+
+function containsDisallowedJSXTextChars(rawStringValue) {
+  return /[{<>}]/.test(rawStringValue);
+}
+
+function containsQuoteCharacters(value) {
+  return /['"]/.test(value);
+}
+
+function containsMultilineComment(value) {
+  return /\/\*/.test(value);
+}
+
+function escapeDoubleQuotes(rawStringValue) {
+  return rawStringValue.replace(/\\"/g, '"').replace(/"/g, '\\"');
+}
+
+function escapeBackslashes(rawStringValue) {
+  return rawStringValue.replace(/\\/g, '\\\\');
+}
+
+function needToEscapeCharacterForJSX(raw, node) {
+  return (
+    containsBackslash(raw)
+    || containsHTMLEntity(raw)
+    || (node.parent.type !== 'JSXAttribute' && containsDisallowedJSXTextChars(raw))
+  );
+}
+
+function containsWhitespaceExpression(child) {
+  if (child.type === 'JSXExpressionContainer') {
+    const value = child.expression.value;
+    return value ? jsxUtil.isWhiteSpaces(value) : false;
+  }
+  return false;
+}
+
+function isLineBreak(text) {
+  return containsLineTerminators(text) && text.trim() === '';
+}
+
+function wrapNonHTMLEntities(text) {
+  const HTML_ENTITY = '<HTML_ENTITY>';
+  const withCurlyBraces = text.split(HTML_ENTITY_REGEX()).map((word) => (
+    word === '' ? '' : `{${JSON.stringify(word)}}`
+  )).join(HTML_ENTITY);
+
+  const htmlEntities = text.match(HTML_ENTITY_REGEX());
+  return htmlEntities.reduce((acc, htmlEntity) => (
+    acc.replace(HTML_ENTITY, htmlEntity)
+  ), withCurlyBraces);
+}
+
+function wrapWithCurlyBraces(rawText) {
+  if (!containsLineTerminators(rawText)) {
+    return `{${JSON.stringify(rawText)}}`;
+  }
+
+  return rawText.split('\n').map((line) => {
+    if (line.trim() === '') {
+      return line;
+    }
+    const firstCharIndex = line.search(/[^\s]/);
+    const leftWhitespace = line.slice(0, firstCharIndex);
+    const text = line.slice(firstCharIndex);
+
+    if (containsHTMLEntity(line)) {
+      return `${leftWhitespace}${wrapNonHTMLEntities(text)}`;
+    }
+    return `${leftWhitespace}{${JSON.stringify(text)}}`;
+  }).join('\n');
+}
+
+function isWhiteSpaceLiteral(node) {
+  return node.type && node.type === 'Literal' && node.value && jsxUtil.isWhiteSpaces(node.value);
+}
+
+function isStringWithTrailingWhiteSpaces(value) {
+  return /^\s|\s$/.test(value);
+}
+
+function isLiteralWithTrailingWhiteSpaces(node) {
+  return node.type && node.type === 'Literal' && node.value && isStringWithTrailingWhiteSpaces(node.value);
+}
+
 // ------------------------------------------------------------------------------
 // Rule Definition
@@ -75,5 +177,4 @@
 
   create(context) {
-    const HTML_ENTITY_REGEX = () => /&[A-Za-z\d#]+;/g;
     const ruleOptions = context.options[0];
     const userConfig = typeof ruleOptions === 'string'
@@ -81,92 +182,4 @@
       : Object.assign({}, DEFAULT_CONFIG, ruleOptions);
 
-    function containsLineTerminators(rawStringValue) {
-      return /[\n\r\u2028\u2029]/.test(rawStringValue);
-    }
-
-    function containsBackslash(rawStringValue) {
-      return arrayIncludes(rawStringValue, '\\');
-    }
-
-    function containsHTMLEntity(rawStringValue) {
-      return HTML_ENTITY_REGEX().test(rawStringValue);
-    }
-
-    function containsOnlyHtmlEntities(rawStringValue) {
-      return rawStringValue.replace(HTML_ENTITY_REGEX(), '').trim() === '';
-    }
-
-    function containsDisallowedJSXTextChars(rawStringValue) {
-      return /[{<>}]/.test(rawStringValue);
-    }
-
-    function containsQuoteCharacters(value) {
-      return /['"]/.test(value);
-    }
-
-    function containsMultilineComment(value) {
-      return /\/\*/.test(value);
-    }
-
-    function escapeDoubleQuotes(rawStringValue) {
-      return rawStringValue.replace(/\\"/g, '"').replace(/"/g, '\\"');
-    }
-
-    function escapeBackslashes(rawStringValue) {
-      return rawStringValue.replace(/\\/g, '\\\\');
-    }
-
-    function needToEscapeCharacterForJSX(raw, node) {
-      return (
-        containsBackslash(raw)
-        || containsHTMLEntity(raw)
-        || (node.parent.type !== 'JSXAttribute' && containsDisallowedJSXTextChars(raw))
-      );
-    }
-
-    function containsWhitespaceExpression(child) {
-      if (child.type === 'JSXExpressionContainer') {
-        const value = child.expression.value;
-        return value ? jsxUtil.isWhiteSpaces(value) : false;
-      }
-      return false;
-    }
-
-    function isLineBreak(text) {
-      return containsLineTerminators(text) && text.trim() === '';
-    }
-
-    function wrapNonHTMLEntities(text) {
-      const HTML_ENTITY = '<HTML_ENTITY>';
-      const withCurlyBraces = text.split(HTML_ENTITY_REGEX()).map((word) => (
-        word === '' ? '' : `{${JSON.stringify(word)}}`
-      )).join(HTML_ENTITY);
-
-      const htmlEntities = text.match(HTML_ENTITY_REGEX());
-      return htmlEntities.reduce((acc, htmlEntity) => (
-        acc.replace(HTML_ENTITY, htmlEntity)
-      ), withCurlyBraces);
-    }
-
-    function wrapWithCurlyBraces(rawText) {
-      if (!containsLineTerminators(rawText)) {
-        return `{${JSON.stringify(rawText)}}`;
-      }
-
-      return rawText.split('\n').map((line) => {
-        if (line.trim() === '') {
-          return line;
-        }
-        const firstCharIndex = line.search(/[^\s]/);
-        const leftWhitespace = line.slice(0, firstCharIndex);
-        const text = line.slice(firstCharIndex);
-
-        if (containsHTMLEntity(line)) {
-          return `${leftWhitespace}${wrapNonHTMLEntities(text)}`;
-        }
-        return `${leftWhitespace}{${JSON.stringify(text)}}`;
-      }).join('\n');
-    }
-
     /**
      * Report and fix an unnecessary curly brace violation on a node
@@ -187,8 +200,12 @@
 
             if (parentType === 'JSXAttribute') {
-              textToReplace = `"${expressionType === 'TemplateLiteral'
-                ? expression.quasis[0].value.raw
-                : expression.raw.slice(1, -1)
-              }"`;
+              if (expressionType !== 'TemplateLiteral' && /["]/.test(expression.raw.slice(1, -1))) {
+                textToReplace = expression.raw;
+              } else {
+                textToReplace = `"${expressionType === 'TemplateLiteral'
+                  ? expression.quasis[0].value.raw
+                  : expression.raw.slice(1, -1)
+                }"`;
+              }
             } else if (jsxUtil.isJSX(expression)) {
               textToReplace = getText(context, expression);
@@ -234,16 +251,4 @@
     }
 
-    function isWhiteSpaceLiteral(node) {
-      return node.type && node.type === 'Literal' && node.value && jsxUtil.isWhiteSpaces(node.value);
-    }
-
-    function isStringWithTrailingWhiteSpaces(value) {
-      return /^\s|\s$/.test(value);
-    }
-
-    function isLiteralWithTrailingWhiteSpaces(node) {
-      return node.type && node.type === 'Literal' && node.value && isStringWithTrailingWhiteSpaces(node.value);
-    }
-
     // Bail out if there is any character that needs to be escaped in JSX
     // because escaping decreases readability and the original code may be more
@@ -269,5 +274,5 @@
           && !needToEscapeCharacterForJSX(expression.raw, JSXExpressionNode) && (
           jsxUtil.isJSX(JSXExpressionNode.parent)
-          || !containsQuoteCharacters(expression.value)
+          || (!containsQuoteCharacters(expression.value) || typeof expression.value === 'string')
         )
       ) {
diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-curly-spacing.js
+++ b/lib/rules/jsx-curly-spacing.js
@@ -36,4 +36,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
diff --git a/lib/rules/jsx-equals-spacing.js b/lib/rules/jsx-equals-spacing.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-equals-spacing.js
+++ b/lib/rules/jsx-equals-spacing.js
@@ -21,4 +21,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
diff --git a/lib/rules/jsx-first-prop-new-line.js b/lib/rules/jsx-first-prop-new-line.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-first-prop-new-line.js
+++ b/lib/rules/jsx-first-prop-new-line.js
@@ -8,4 +8,5 @@
 const docsUrl = require('../util/docsUrl');
 const report = require('../util/report');
+const propsUtil = require('../util/props');
 
 // ------------------------------------------------------------------------------
@@ -56,5 +57,6 @@
                 node: decl,
                 fix(fixer) {
-                  return fixer.replaceTextRange([(node.typeParameters || node.name).range[1], decl.range[0]], '\n');
+                  const nodeTypeArguments = propsUtil.getTypeArguments(node);
+                  return fixer.replaceTextRange([(nodeTypeArguments || node.name).range[1], decl.range[0]], '\n');
                 },
               });
diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-fragments.js
+++ b/lib/rules/jsx-fragments.js
@@ -23,10 +23,10 @@
 
 const messages = {
-  fragmentsNotSupported: 'Fragments are only supported starting from React v16.2. '
-    + 'Please disable the `react/jsx-fragments` rule in `eslint` settings or upgrade your version of React.',
+  fragmentsNotSupported: 'Fragments are only supported starting from React v16.2. Please disable the `react/jsx-fragments` rule in `eslint` settings or upgrade your version of React.',
   preferPragma: 'Prefer {{react}}.{{fragment}} over fragment shorthand',
   preferFragment: 'Prefer fragment shorthand over {{react}}.{{fragment}}',
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -171,5 +171,5 @@
         if (node.source && node.source.value === 'react') {
           node.specifiers.forEach((spec) => {
-            if (spec.imported && spec.imported.name === fragmentPragma) {
+            if ('imported' in spec && spec.imported && spec.imported.name === fragmentPragma) {
               if (spec.local) {
                 fragmentNames.add(spec.local.name);
diff --git a/lib/rules/jsx-indent-props.js b/lib/rules/jsx-indent-props.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-indent-props.js
+++ b/lib/rules/jsx-indent-props.js
@@ -46,4 +46,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -117,6 +118,6 @@
      * Reports a given indent violation and properly pluralizes the message
      * @param {ASTNode} node Node violating the indent rule
-     * @param {Number} needed Expected indentation character count
-     * @param {Number} gotten Indentation character count in the actual node/code
+     * @param {number} needed Expected indentation character count
+     * @param {number} gotten Indentation character count in the actual node/code
      */
     function report(node, needed, gotten) {
@@ -142,5 +143,5 @@
      * Get node indent
      * @param {ASTNode} node Node to examine
-     * @return {Number} Indent
+     * @return {number} Indent
      */
     function getNodeIndent(node) {
@@ -174,5 +175,5 @@
      * Check indent for nodes list
      * @param {ASTNode[]} nodes list of node objects
-     * @param {Number} indent needed indent
+     * @param {number} indent needed indent
      */
     function checkNodesIndent(nodes, indent) {
diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js
index v7.35.0..v7.37.2 100644
--- a/lib/rules/jsx-indent.js
+++ b/lib/rules/jsx-indent.js
@@ -51,4 +51,5 @@
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -106,5 +107,5 @@
      * Responsible for fixing the indentation issue fix
      * @param {ASTNode} node Node violating the indent rule
-     * @param {Number} needed Expected indentation character count
+     * @param {number} needed Expected indentation character count
      * @returns {Function} function to be executed by the fixer
      * @private
@@ -147,6 +148,6 @@
      * Reports a given indent violation and properly pluralizes the message
      * @param {ASTNode} node Node violating the indent rule
-     * @param {Number} needed Expected indentation character count
-     * @param {Number} gotten Indentation character count in the actual node/code
+     * @param {number} needed Expected indentation character count
+     * @param {number} gotten Indentation character count in the actual node/code
      * @param {Object} [loc] Error line and column location
      */
@@ -169,7 +170,7 @@
      * Get node indent
      * @param {ASTNode} node Node to examine
-     * @param {Boolean} [byLastLine] get indent of node's last line
-     * @param {Boolean} [excludeCommas] skip comma on start of line
-     * @return {Number} Indent
+     * @param {boolean} [byLastLine] get indent of node's last line
+     * @param {boolean} [excludeCommas] skip comma on start of line
+     * @return {number} Indent
      */
     function getNodeIndent(node, byLastLine, excludeCommas) {
@@ -198,5 +199,5 @@
      * Check if the node is the right member of a logical expression
      * @param {ASTNode} node The node to check
-     * @return {Boolean} true if its the case, false if not
+     * @return {boolean} true if its the case, false if not
      */
     function isRightInLogicalExp(node) {
@@ -213,5 +214,5 @@
      * Check if the node is the alternate member of a conditional expression
      * @param {ASTNode} node The node to check
-     * @return {Boolean} true if its the case, false if not
+     * @return {boolean} true if its the case, false if not
      */
     function isAlternateInConditionalExp(node) {
@@ -228,5 +229,5 @@
      * Check if the node is within a DoExpression block but not the first expression (which need to be indented)
      * @param {ASTNode} node The node to check
-     * @return {Boolean} true if its the case, false if not
+     * @return {boolean} true if its the case, false if not
      */
     function isSecondOrSubsequentExpWithinDoExp(node) {
@@ -299,6 +300,6 @@
      * Check indent for nodes list
      * @param {ASTNode} node The node to check
-     * @param {Number} indent needed indent
-     * @param {Boolean} [excludeCommas] skip comma on start of line
+     * @param {number} indent needed indent
+     * @param {boolean} [excludeCommas] skip comma on start of line
      */
     function checkNodesIndent(node, indent, excludeCommas) {
@@ -319,5 +320,5 @@
      * Check indent for Literal Node or JSXText Node
      * @param {ASTNode} node The node to check
-     * @param {Number

(too long so truncated)

Size Files
810.5 KB → 919.6 KB (+109.1 KB 🟡) 139 → 406 (+267 🟡)
Command details
npm diff --diff=eslint-plugin-react@7.35.0 --diff=eslint-plugin-react@7.37.2 --diff-unified=2

See also the npm diff document.

Reported by ybiquitous/npm-diff-action@v1.6.0 (Node.js 22.11.0 and npm 10.9.0)

@dependabot dependabot bot force-pushed the dependabot/npm_and_yarn/eslint-plugin-react-7.37.2 branch 2 times, most recently from 6cb276d to 121a49e Compare November 1, 2024 03:08
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.35.0 to 7.37.2.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](jsx-eslint/eslint-plugin-react@v7.35.0...v7.37.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot bot force-pushed the dependabot/npm_and_yarn/eslint-plugin-react-7.37.2 branch from 121a49e to d73e796 Compare November 1, 2024 03:10
@ybiquitous ybiquitous changed the title build(deps): bump eslint-plugin-react from 7.35.0 to 7.37.2 feat(deps): bump eslint-plugin-react from 7.35.0 to 7.37.2 Nov 5, 2024
@github-actions github-actions bot merged commit dc57baf into main Nov 5, 2024
7 checks passed
@github-actions github-actions bot deleted the dependabot/npm_and_yarn/eslint-plugin-react-7.37.2 branch November 5, 2024 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant